/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/


//============================================================================
/// \file   AutoHideSideBar.cpp
/// \author Syarif Fakhri
/// \date   05.09.2022
/// \brief  Implementation of CAutoHideSideBar class
//============================================================================


//============================================================================
//                                   INCLUDES
//============================================================================
#include "AutoHideSideBar.h"

#include <QBoxLayout>
#include <QStyleOption>
#include <QPainter>
#include <QXmlStreamWriter>

#include "DockContainerWidget.h"
#include "DockWidgetTab.h"
#include "DockFocusController.h"
#include "AutoHideDockContainer.h"
#include "DockAreaWidget.h"
#include "DockingStateReader.h"
#include "AutoHideTab.h"

namespace ads {
    class CTabsWidget;
    
    /**
     * CSideTabBar类的私有数据类（pimpl）
     */
    struct AutoHideSideBarPrivate {
        /**
         * 私有数据构造函数
         */
        AutoHideSideBarPrivate(CAutoHideSideBar* _public);
        CAutoHideSideBar* _this;
        CDockContainerWidget* ContainerWidget;
        CTabsWidget* TabsContainerWidget;
        QBoxLayout* TabsLayout;
        Qt::Orientation Orientation;
        SideBarLocation SideTabArea = SideBarLocation::SideBarLeft;
        
        /**
         * 方便的函数，用于检查是否为水平侧边栏
         */
        bool isHorizontal() const {
            return Qt::Horizontal == Orientation;
        }
        
        /**
         * 从视口调用以将事件处理转发给此类
         */
        void handleViewportEvent(QEvent* e);
    }; // struct AutoHideSideBarPrivate
    
    /**
     * 此小部件存储选项卡按钮
     */
    class CTabsWidget : public QWidget {
    public:
        using QWidget::QWidget;
        using Super = QWidget;
        AutoHideSideBarPrivate* EventHandler;
        
        /**
         * 返回最小大小提示作为最小大小提示
         */
        virtual QSize minimumSizeHint() const override {
            return Super::sizeHint();
        }
        
        /**
         * 将事件处理转发给EventHandler
         */
        virtual bool event(QEvent* e) override {
            EventHandler->handleViewportEvent(e);
            return Super::event(e);
        }
    };
    
    //============================================================================
    
    AutoHideSideBarPrivate::AutoHideSideBarPrivate(CAutoHideSideBar* _public) :
        _this(_public) {
    }
    
    //============================================================================
    
    void AutoHideSideBarPrivate::handleViewportEvent(QEvent* e) {
        switch (e->type()) {
            case QEvent::ChildRemoved:
                if (TabsLayout->isEmpty()) {
                    _this->hide();
                }
                break;
            default:
                break;
        }
    }
    
    //============================================================================
    
    CAutoHideSideBar::CAutoHideSideBar(CDockContainerWidget* parent, SideBarLocation area) :
        Super(parent),
        d(new AutoHideSideBarPrivate(this)) {
        d->SideTabArea = area;
        d->ContainerWidget = parent;
        d->Orientation = (area == SideBarLocation::SideBarBottom || area == SideBarLocation::SideBarTop)
            ? Qt::Horizontal : Qt::Vertical;
        
        setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
        setFrameStyle(QFrame::NoFrame);
        setWidgetResizable(true);
        setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        
        d->TabsContainerWidget = new CTabsWidget();
        d->TabsContainerWidget->EventHandler = d;
        d->TabsContainerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
        d->TabsContainerWidget->setObjectName("sideTabsContainerWidget");
        
        d->TabsLayout = new QBoxLayout(d->Orientation == Qt::Vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
        d->TabsLayout->setContentsMargins(0, 0, 0, 0);
        d->TabsLayout->setSpacing(12);
        d->TabsLayout->addStretch(1);
        d->TabsContainerWidget->setLayout(d->TabsLayout);
        
        setWidget(d->TabsContainerWidget);
        setFocusPolicy(Qt::NoFocus);
        
        if (d->isHorizontal()) {
            setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
        } else {
            setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
        }
        
        hide();
    }
    
    //============================================================================
    
    CAutoHideSideBar::~CAutoHideSideBar() {
        ADS_PRINT("~CSideTabBar()");
        
        // SideTabeBar不是选项卡的所有者，为了防止删除，我们在这里将父对象设置为nullptr以将其从子对象中移除
        auto Tabs = findChildren<CAutoHideTab*>(QString(), Qt::FindDirectChildrenOnly);
        for (auto Tab : Tabs) {
            Tab->setParent(nullptr);
        }
        
        delete d;
    }
    
    //============================================================================
    
    void CAutoHideSideBar::insertTab(int Index, CAutoHideTab* SideTab) {
        SideTab->setSideBar(this);
        SideTab->installEventFilter(this);
        
        // 默认插入是追加
        if (Index < 0) {
            d->TabsLayout->insertWidget(d->TabsLayout->count() - 1, SideTab);
        } else {
            d->TabsLayout->insertWidget(Index, SideTab);
        }
        
        show();
    }
    
    //============================================================================
    
    CAutoHideDockContainer* CAutoHideSideBar::insertDockWidget(int Index, CDockWidget* DockWidget) {
        auto AutoHideContainer = new CAutoHideDockContainer(DockWidget, d->SideTabArea, d->ContainerWidget);
        DockWidget->dockManager()->dockFocusController()->clearDockWidgetFocus(DockWidget);
        auto Tab = AutoHideContainer->autoHideTab();
        DockWidget->setSideTabWidget(Tab);
        insertTab(Index, Tab);
        return AutoHideContainer;
    }
    
    //============================================================================
    
    void CAutoHideSideBar::removeAutoHideWidget(CAutoHideDockContainer* AutoHideWidget) {
        AutoHideWidget->autoHideTab()->removeFromSideBar();
        auto DockContainer = AutoHideWidget->dockContainer();
        if (DockContainer) {
            DockContainer->removeAutoHideWidget(AutoHideWidget);
        }
        AutoHideWidget->setParent(nullptr);
    }
    
    //============================================================================
    
    void CAutoHideSideBar::addAutoHideWidget(CAutoHideDockContainer* AutoHideWidget, int TabIndex) {
        auto SideBar = AutoHideWidget->autoHideTab()->sideBar();
        if (SideBar == this) {
            // 如果移动到相同的选项卡索引或者在下一个选项卡索引之前插入，则最终位置将相同，可以离开
            if (AutoHideWidget->tabIndex() == TabIndex || (AutoHideWidget->tabIndex() + 1) == TabIndex) {
                return;
            }
            
            // 我们在下面的代码中从侧边栏中移除此自动隐藏小部件，因此在此处需要修正TabIndex
            if (AutoHideWidget->tabIndex() < TabIndex) {
                --TabIndex;
            }
        }
        
        if (SideBar) {
            SideBar->removeAutoHideWidget(AutoHideWidget);
        }
        
        AutoHideWidget->setParent(d->ContainerWidget);
        AutoHideWidget->setSideBarLocation(d->SideTabArea);
        d->ContainerWidget->registerAutoHideWidget(AutoHideWidget);
        insertTab(TabIndex, AutoHideWidget->autoHideTab());
    }
}

//============================================================================
void CAutoHideSideBar::removeTab(CAutoHideTab* SideTab)
{
	SideTab->removeEventFilter(this);
    d->TabsLayout->removeWidget(SideTab);
    if (d->TabsLayout->isEmpty())
    {
    	hide();
    }
}


namespace ads {
    class CTabsWidget;
    
    /**
     * CSideTabBar类的私有数据类（pimpl）
     */
    struct AutoHideSideBarPrivate {
        /**
         * 私有数据构造函数
         */
        AutoHideSideBarPrivate(CAutoHideSideBar* _public);
        CAutoHideSideBar* _this;
        CDockContainerWidget* ContainerWidget;
        CTabsWidget* TabsContainerWidget;
        QBoxLayout* TabsLayout;
        Qt::Orientation Orientation;
        SideBarLocation SideTabArea = SideBarLocation::SideBarLeft;
        
        /**
         * 方便的函数，用于检查是否为水平侧边栏
         */
        bool isHorizontal() const {
            return Qt::Horizontal == Orientation;
        }
        
        /**
         * 从视口调用以将事件处理转发给此类
         */
        void handleViewportEvent(QEvent* e);
    }; // struct AutoHideSideBarPrivate
    
    /**
     * 此小部件存储选项卡按钮
     */
    class CTabsWidget : public QWidget {
    public:
        using QWidget::QWidget;
        using Super = QWidget;
        AutoHideSideBarPrivate* EventHandler;
        
        /**
         * 返回最小大小提示作为最小大小提示
         */
        virtual QSize minimumSizeHint() const override {
            return Super::sizeHint();
        }
        
        /**
         * 将事件处理转发给EventHandler
         */
        virtual bool event(QEvent* e) override {
            EventHandler->handleViewportEvent(e);
            return Super::event(e);
        }
    };
    
    //============================================================================
    
    AutoHideSideBarPrivate::AutoHideSideBarPrivate(CAutoHideSideBar* _public) :
        _this(_public) {
    }
    
    //============================================================================
    
    void AutoHideSideBarPrivate::handleViewportEvent(QEvent* e) {
        switch (e->type()) {
            case QEvent::ChildRemoved:
                if (TabsLayout->isEmpty()) {
                    _this->hide();
                }
                break;
            default:
                break;
        }
    }
    
    //============================================================================
    
    CAutoHideSideBar::CAutoHideSideBar(CDockContainerWidget* parent, SideBarLocation area) :
        Super(parent),
        d(new AutoHideSideBarPrivate(this)) {
        d->SideTabArea = area;
        d->ContainerWidget = parent;
        d->Orientation = (area == SideBarLocation::SideBarBottom || area == SideBarLocation::SideBarTop)
            ? Qt::Horizontal : Qt::Vertical;
        
        setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
        setFrameStyle(QFrame::NoFrame);
        setWidgetResizable(true);
        setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        
        d->TabsContainerWidget = new CTabsWidget();
        d->TabsContainerWidget->EventHandler = d;
        d->TabsContainerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
        d->TabsContainerWidget->setObjectName("sideTabsContainerWidget");
        
        d->TabsLayout = new QBoxLayout(d->Orientation == Qt::Vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
        d->TabsLayout->setContentsMargins(0, 0, 0, 0);
        d->TabsLayout->setSpacing(12);
        d->TabsLayout->addStretch(1);
        d->TabsContainerWidget->setLayout(d->TabsLayout);
        
        setWidget(d->TabsContainerWidget);
        setFocusPolicy(Qt::NoFocus);
        
        if (d->isHorizontal()) {
            setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
        } else {
            setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
        }
        
        hide();
    }
    
    //============================================================================
    
    CAutoHideSideBar::~CAutoHideSideBar() {
        ADS_PRINT("~CSideTabBar()");
        
        // SideTabeBar不是选项卡的所有者，为了防止删除，我们在这里将父对象设置为nullptr以将其从子对象中移除
        auto Tabs = findChildren<CAutoHideTab*>(QString(), Qt::FindDirectChildrenOnly);
        for (auto Tab : Tabs) {
            Tab->setParent(nullptr);
        }
        
        delete d;
    }
    
    //============================================================================
    
    void CAutoHideSideBar::insertTab(int Index, CAutoHideTab* SideTab) {
        SideTab->setSideBar(this);
        SideTab->installEventFilter(this);
        
        // 默认插入是追加
        if (Index < 0) {
            d->TabsLayout->insertWidget(d->TabsLayout->count() - 1, SideTab);
        } else {
            d->TabsLayout->insertWidget(Index, SideTab);
        }
        
        show();
    }
    
    //============================================================================
    
    CAutoHideDockContainer* CAutoHideSideBar::insertDockWidget(int Index, CDockWidget* DockWidget) {
        auto AutoHideContainer = new CAutoHideDockContainer(DockWidget, d->SideTabArea, d->ContainerWidget);
        DockWidget->dockManager()->dockFocusController()->clearDockWidgetFocus(DockWidget);
        auto Tab = AutoHideContainer->autoHideTab();
        DockWidget->setSideTabWidget(Tab);
        insertTab(Index, Tab);
        return AutoHideContainer;
    }
    
    //============================================================================
    
    void CAutoHideSideBar::removeAutoHideWidget(CAutoHideDockContainer* AutoHideWidget) {
        AutoHideWidget->autoHideTab()->removeFromSideBar();
        auto DockContainer = AutoHideWidget->dockContainer();
        if (DockContainer) {
            DockContainer->removeAutoHideWidget(AutoHideWidget);
        }
        AutoHideWidget->setParent(nullptr);
    }
    
    //============================================================================
    
    void CAutoHideSideBar::addAutoHideWidget(CAutoHideDockContainer* AutoHideWidget, int TabIndex) {
        auto SideBar = AutoHideWidget->autoHideTab()->sideBar();
        if (SideBar == this) {
            // 如果移动到相同的选项卡索引或者在下一个选项卡索引之前插入，则最终位置将相同，可以离开
            if (AutoHideWidget->tabIndex() == TabIndex || (AutoHideWidget->tabIndex() + 1) == TabIndex) {
                return;
            }
            
            // 我们在下面的代码中从侧边栏中移除此自动隐藏小部件，因此在此处需要修正TabIndex
            if (AutoHideWidget->tabIndex() < TabIndex) {
                --TabIndex;
            }
        }
        
        if (SideBar) {
            SideBar->removeAutoHideWidget(AutoHideWidget);
        }
        
        AutoHideWidget->setParent(d->ContainerWidget);
        AutoHideWidget->setSideBarLocation(d->SideTabArea);
        d->ContainerWidget->registerAutoHideWidget(AutoHideWidget);
        insertTab(TabIndex, AutoHideWidget->autoHideTab());
    }
}

} // namespace ads

